#include "../include/WebPageQuery.h"
#include "../include/WebPage.h"
#include "../include/PageLibPreprocessor.h"

#include <sstream>
#include <iostream>
#include <cmath>
#include <unordered_map>

using std::istringstream;
using std::unordered_map;
using std::make_pair;
using std::cout;
using std::endl;

WebPageQuery::WebPageQuery() {

}

WebPageQuery::~WebPageQuery() {

}

string WebPageQuery::doQuery(const string &str) {
    /* LoadLibrary(); */

    timespec startTime;
    timespec endTime;
    clock_gettime(CLOCK_REALTIME, &startTime);
    //根据客户端输入字符串解析其中的关键词。
    vector<string> queryWords;
    string word;
    istringstream iss(str);
    while (iss >> word) {
        queryWords.push_back(word);
    }

    //获取基准向量
    vector<double> baseVector = getQueryWordsWeightVector(queryWords);
    double baseTmp = 0;     //baseTmp = |X|
    for (auto &elem: baseVector) {
        baseTmp += (elem * elem);
    }
    baseTmp = sqrt(baseTmp);

    //查询包含所有关键词的网页
    map<int, vector<double>> resultVec;
    if (executeQuery(queryWords, resultVec)) {
        //存放符合搜索条件的每篇文章的余弦相似度
        multimap<double,int,greater<double>> cosOfEveryEssay;    //注意要用multimap
        //对网页根据余弦相似度进行排序
        for (auto &elem: resultVec) {
            double tmp1 = 0;    //tmp1 = x1*y1 + x2*y2 + ... + x3*y3
            double tmp2 = 0;    //tmp2 = |Y|
            for (auto it = baseVector.begin(), jt = (elem.second).begin();
                 (it != baseVector.end())&&(jt != baseVector.end()); ++it, ++jt) {
                tmp1 += (*it) * (*jt);
                tmp2 += (*it) * (*it);
            }
            tmp2 = sqrt(tmp2);
            double cosValue = tmp1 / (tmp2 * baseTmp);
            cosOfEveryEssay.insert({cosValue,elem.first});
        }

        // //插入排序根据余弦相似度进行排序
        // for (int i = 1; i < cosOfEveryEssay.size(); ++i) {
        //     int j = i - 1;
        //     double k2 = cosOfEveryEssay[i].second;
        //     int k1 = cosOfEveryEssay[i].first;
        //     while (j > 0 && cosOfEveryEssay[j].second < k2) {
        //         cosOfEveryEssay[j+1] = cosOfEveryEssay[j];
        //         j--;
        //     }
        //     cosOfEveryEssay[j+1].first = k1;
        //     cosOfEveryEssay[j+1].second = k2;
        // }

        //取出cos最大的前三个文章
        int count = 0;
        vector<int> ReturnEssay;
        for (auto it = cosOfEveryEssay.begin(); it != cosOfEveryEssay.end(); ++it) {
            ReturnEssay.push_back((*it).second);
            count++;
            if (count >= 3) {
                break;
            }
        }

        string returnstr = createJson(ReturnEssay, queryWords);
        

        clock_gettime(CLOCK_REALTIME, &endTime);
        std::cout << "搜索耗时：" << endTime.tv_sec - startTime.tv_sec + (endTime.tv_nsec - startTime.tv_nsec) * 0.000000001 << " 秒" << std::endl;
    }
    else {
        cout << returnNoAnswer() << endl;
    }
}

void WebPageQuery::LoadLibrary() {
    //读取偏移库
    ifstream ifsOff("../newdata/offset.dat");
    string line;
    while (getline(ifsOff, line)) {
        istringstream iss(line);
        int id, init, offset;
        iss >> id >> init >> offset;
        _offsetLib[id] = make_pair(init, offset);
    }
    ifsOff.close();

    //读取倒排索引库
    ifstream ifsInvert("../newdata/invertIndex.txt");
    if (!ifsInvert.good())
    {
        cout << "error" << endl;
    }
    string lin;
    while (getline(ifsInvert, lin))
    {
        istringstream is(lin);
        string word;
        int id;
        double w;
        is >> word;
        while (is >> id >> w)
        {
            _invertIndexTable[word].insert(make_pair(id, w));
        }
    }
    ifsInvert.close();
}

vector<double> WebPageQuery::getQueryWordsWeightVector(vector<string> &queryWords) {
    vector<double> finalWeight;
    finalWeight.resize(queryWords.size());
    // cout<<"词语数量："<<finalWeight.size()<<endl;

    map<string,int> tmpMap;
    for(auto word:queryWords){
        ++tmpMap[word];
    }
    double sumWeight = 0;
    int N = _offsetLib.size();
    for(int i=0;i<queryWords.size();++i){
        int TF = tmpMap[queryWords[i]];     //该词在该文章中出现的次数
        int DF = _invertIndexTable[queryWords[i]].size(); //包含该词的文章总数   
        double IDF = log2((double)N/(DF+1));
        finalWeight[i] = (double)TF*IDF;
        sumWeight += (finalWeight[i]*finalWeight[i]);
    }
    for(int i = 0; i < finalWeight.size(); ++i){
        finalWeight[i] = finalWeight[i] / sqrt(sumWeight);
    }
    return finalWeight;
}

bool WebPageQuery::executeQuery(const vector<string> &queryWords, map<int, vector<double>> &resultVec) {
    for (int i = 0; i < queryWords.size(); ++i) {
        auto it = _invertIndexTable.find(queryWords[i]);
        if (it == _invertIndexTable.end()) {
            return false;
        }
		auto &Map = _invertIndexTable[queryWords[i]];
		for (auto pair : Map) {
			if (resultVec.find(pair.first) == resultVec.end()) {
				resultVec[pair.first].resize(queryWords.size(), 0);
			}
			resultVec[pair.first][i] = pair.second;
		}
	}
    if (resultVec.empty()) {
        return false;
    }
    return true;
}

string WebPageQuery::createJson(vector<int> &docIdVec, const vector<string> &queryWords) {
        //取出文章
    ifstream ifs;
    ifs.open("../newdata/rigepage.dat");
    string str;
    for (auto it = docIdVec.begin(); it != docIdVec.end(); ++it) {
        // cout << "docid: " << *it << endl;
        int a = _offsetLib[*it].first;
        int b = _offsetLib[*it].second;
        // cout << "init: " << a << ", off: " << b << endl;
        
        ifs.seekg(a);
        char mybuf[65535] = {0};
        ifs.read(mybuf, b);
        string buf = mybuf;
        string title, content;
        size_t contentBegin = buf.find("<content>");
        size_t contentEnd = buf.find("</content>");
        content = buf.substr(contentBegin + 9, contentEnd - contentBegin - 9);

        size_t titleBegin = buf.find("<title>");
        size_t titleEnd = buf.find("</title>");
        title = buf.substr(titleBegin + 7, titleEnd - titleBegin - 7);

        str += "Title: \n" + title + "\nContent: \n" + content + "\n";
        
    }
    ifs.close();
    string finalStr = str; 
    // cout << finalStr << endl;
    //存储？
    //redis


    return finalStr;
    //此为最终的查询结果
}

string WebPageQuery::returnNoAnswer() {
    return "Not found";
}
